﻿#region Copyright RenGuiYou. All rights reserved.
//=====================================================
// NeatlyFrameWork
// Author:      RenGuiyou
// Feedback: 	mailto:750539605@qq.com
//=====================================================
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using MiniJson;
using Neatly;
using Pandora;
using PandoraEditor;
using UnityEditor;
using UnityEngine;

namespace NeatlyEditor
{
    public static class NeatlyAssetPacker
    {
        public static void BuildLua()
        {
            LuaBuilder.Build();
            Debug.Log("Lua build complete.");
        }

        public static void BuildLuaSource()
        {
            LuaBuilder.BuildSource();
            Debug.Log("Lua source build complete.");
        }

        public static void BuildAssetBundle()
        {
            AssetBundleImport.Init();
#if UNITY_WEBGL
            AssetBundleImport.ExportLua();
#endif
            AssetBundleImport.ExportAtlas();
            AssetBundleImport.ExportUIPanel();
            AssetBundleImport.ExportScene();
            AssetBundleImport.ImportSingle("Config");
            AssetBundleImport.ImportSingle("data/global");
            var setting = BuildSetting.AssetsBuildSetting;
            if (setting.build_shader_variant)
            {
                AssetBundleImport.ImportSingle("data/variant");
            }
            if (setting.build_bm_font)
            {
                AssetBundleImport.ImportMultiCatalog("data/font_bm");
            }
            if (setting.build_music)
            {
                AssetBundleImport.ExportMusic();
            }
            if (setting.build_sound)
            {
                AssetBundleImport.ExportSound();
            }
            if (setting.build_voice)
            {
                AssetBundleImport.ExportVoice();
            }
            if (setting.build_video)
            {
                AssetBundleImport.ExportVideo();
            }
            foreach (var v in setting.SinglePackerArray())
            {
                if (!String.IsNullOrEmpty(v))
                {
                    AssetBundleImport.ImportSingle(v);
                }
            }
            foreach (var v in setting.MultiCatalogPackerArray())
            {
                if (!String.IsNullOrEmpty(v))
                {
                    AssetBundleImport.ImportMultiCatalog(v);
                }
            }
            foreach (var v in setting.MultiFileArray())
            {
                if (!String.IsNullOrEmpty(v))
                {
                    AssetBundleImport.ImportMultiFile(v);
                }
            }
            //写入映射关系
            AssetBundleImport.WriteBundlePathNameMap();
            AssetBundleManifest manifest = AssetBundleExport.ExportAssetBundle(AssetBundleImport.m_BuildAssetMap);
            Debug.Log("AssetBundle build complete.");
            //检测依赖关系
            NeatlyAssetBundleCheck.CheckAssetBundleDependency(manifest);
        }

        public static void BuildAllAsset()
        {
            ConfigDataEditor.UpdateConfigData();
            BuildLua();
            BuildAssetBundle();
            BuildAssetsHash();
        }

        public static void BuildAllAssetSourceLua()
        {
            BuildLuaSource();
            BuildAssetBundle();
            BuildAssetsHash();
        }

        public static void BuildAssetsHash()
        {
            PdrFileUtil.DeleteDirectory(Application.streamingAssetsPath);
            PdrFileUtil.DeleteDirectory(NeatlyEditorConfig.PATH_EXPORT_BUNDLE);
#if !UNITY_WEBGL
            foreach (var folder in BuildSetting.AssetsBuildSetting.CopyFolderArray())
            {
                if (string.IsNullOrEmpty(folder)) continue;
                var outputPath = $"{NeatlyEditorConfig.PATH_TEMP}/{folder}";
                PdrFileUtil.DeleteDirectory(outputPath);
                PdrFileUtil.CopyDirectory($"{Application.dataPath}/{folder}", outputPath);
            }
#endif
            var fileList = PdrFileUtil.GetFilesName(NeatlyEditorConfig.PATH_TEMP);
            Dictionary<string, Dictionary<string, byte[]>> folderHashMap = new();
            Dictionary<string, Dictionary<string, int>> folderSizeMap = new();
            string pathExportBundle = Path.Combine(NeatlyEditorConfig.PATH_EXPORT_BUNDLE, CommandParam.Version.Split(".")[0]);
#if UNITY_WEBGL
            pathExportBundle+= "/StreamingAssets";
#endif
            foreach (var file in fileList)
            {
                var filePath = file.Replace("\\", "/");
                var folder = Path.GetFileName(Path.GetDirectoryName(filePath)).ToLower();
                if (folder == NeatlyConfig.LUA_PACKAGE_32)
                {
                    continue;
                }
                byte[] md5Bytes = HashHelper.FileMD5Bytes(filePath);
                string md5String = HashHelper.ToHexString(md5Bytes);
                var fileName = Path.GetFileNameWithoutExtension(filePath).ToLower();
                var outputStreamingPath = $"{Path.Combine(Application.streamingAssetsPath, folder, fileName)}_{md5String}";
                var outputExportBundlePath = $"{Path.Combine(pathExportBundle, folder, fileName)}_{md5String}";
                var fileNameArray = filePath.Split(".");
                if (fileNameArray.Length > 1)
                {
                    outputStreamingPath += "." + fileNameArray[1];
                    outputExportBundlePath += "." + fileNameArray[1];
                }
                outputStreamingPath = outputStreamingPath.Replace("\\", "/");
                outputExportBundlePath = outputExportBundlePath.Replace("\\", "/");
                PdrFileUtil.CopyFile(filePath, outputExportBundlePath);
                // AssetBundleOffsetEncryption(outputExportBundlePath);
                PdrFileUtil.CopyFile(outputExportBundlePath, outputStreamingPath);
                //Lua64的时候兼顾Lua32
                if (folder == NeatlyConfig.LUA_PACKAGE_64)
                {
                    var lua32InputPath = filePath.Replace($"/{NeatlyConfig.LUA_PACKAGE_64}/", $"/{NeatlyConfig.LUA_PACKAGE_32}/");
                    PdrFileUtil.CopyFile(lua32InputPath, outputStreamingPath.Replace($"/{NeatlyConfig.LUA_PACKAGE_64}/", $"/{NeatlyConfig.LUA_PACKAGE_32}/"));
                    PdrFileUtil.CopyFile(lua32InputPath, outputExportBundlePath.Replace($"/{NeatlyConfig.LUA_PACKAGE_64}/", $"/{NeatlyConfig.LUA_PACKAGE_32}/"));
                }
                if (!folderHashMap.TryGetValue(folder, out var fileHashMap))
                {
                    fileHashMap = new Dictionary<string, byte[]>();
                    folderHashMap.Add(folder, fileHashMap);
                }
                fileHashMap.Add(fileName, md5Bytes);
#if !UNITY_WEBGL
                if (!folderSizeMap.TryGetValue(folder, out var fileSizeMap))
                {
                    fileSizeMap = new Dictionary<string, int>();
                    folderSizeMap.Add(folder, fileSizeMap);
                }
                FileInfo fileInfo = new FileInfo(outputStreamingPath);
                fileSizeMap.Add(fileName, (int)fileInfo.Length);
#endif
            }
#if UNITY_WEBGL
            string hashLabel = CreateHashLabel(folderHashMap);
            File.WriteAllText(Path.Combine(NeatlyEditorConfig.PATH_EXPORT_BUNDLE, NeatlyConfig.FILE_TXT_VERSION), hashLabel);
#else
            var hashArray = CreateHashByteArray(folderHashMap, folderSizeMap);
            File.WriteAllBytes(Path.Combine(Application.streamingAssetsPath, NeatlyConfig.FILE_HASH), hashArray);
            File.WriteAllBytes(Path.Combine(pathExportBundle, NeatlyConfig.FILE_HASH), hashArray);
            File.WriteAllBytes(Path.Combine(pathExportBundle, NeatlyConfig.GetFileHashName(CommandParam.PatchCode)), hashArray);
            string versionInfo = "{" + $"\"app_version\":\"{CommandParam.Version}.{CommandParam.PatchCode}\", \"res_version\":\"{CommandParam.PatchCode}\"" + "}";
            string labelVersion = "{" + $"\"1\":{versionInfo}, \"2\":{versionInfo}, \"3\":{versionInfo}" + "}";
            File.WriteAllText(Path.Combine(NeatlyEditorConfig.PATH_EXPORT_BUNDLE, NeatlyConfig.FILE_TXT_VERSION), labelVersion);
#endif
            AssetDatabase.Refresh();
            Debug.Log("File Hash build complete.");
        }

        private static string CreateHashLabel(Dictionary<string, Dictionary<string, byte[]>> folderHashMap)
        {
            JsonValue json = new JsonValue();
            var versionArray = CommandParam.Version.Split(".");
            json.Add("major", versionArray[0]);
            json.Add("minor", versionArray[1]);
            json.Add("patch", CommandParam.PatchCode);
            foreach (var folderKv in folderHashMap)
            {
                foreach (var fileKv in folderKv.Value)
                {
                    json.Add(fileKv.Key, HashHelper.ToHexString(fileKv.Value));
                }
            }
            return JsonWriter.Write(json);
        }

        private static byte[] CreateHashByteArray(Dictionary<string, Dictionary<string, byte[]>> folderHashMap, Dictionary<string, Dictionary<string, int>> folderSizeMap)
        {
            HashtWriter writer = new();
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1);
            int buildTime = Convert.ToInt32(ts.TotalSeconds);
            writer.Write(buildTime);
            var versionArray = CommandParam.Version.Split(".");
            if (versionArray.Length != 2)
            {
                Debug.LogError("版本号输入错误!!!");
                return null;
            }
            foreach (var ver in versionArray)
            {
                writer.Write(int.Parse(ver));
            }
            writer.Write(CommandParam.PatchCode);
            writer.Write(folderHashMap.Count);
            foreach (var folderKv in folderHashMap)
            {
                string folderName = folderKv.Key;
                writer.Write(folderName);
                writer.Write(folderKv.Value.Count);
                foreach (var fileKv in folderKv.Value)
                {
                    string fileName = fileKv.Key;
                    writer.Write(fileName);
                    writer.WriteMd5(fileKv.Value);
                    writer.Write(folderSizeMap[folderName][fileName]);
                }
            }
            return writer.GetBytes().ToArray();
        }

        private static void AssetBundleOffsetEncryption(string filePath)
        {
            byte[] sourceArray = File.ReadAllBytes(filePath);
            int targetLength = NeatlyConfig.BUNDLE_OFFSET + sourceArray.Length;
            var targetArray = new byte[targetLength];
            for (int i = 0; i < sourceArray.Length; i++)
            {
                targetArray[NeatlyConfig.BUNDLE_OFFSET + i] = sourceArray[i];
            }
            FileStream fs = File.OpenWrite(filePath);
            fs.Write(targetArray, 0, targetLength);
            fs.Close();
        }
    }
}